/*
 * kmean_main.cpp
 *
 *  Created on: Mar 06, 2011
 *      Author: tqlong
 */

#include <iostream>
#include <boost/program_options.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <ctime>
#include <cassert>
#include <armadillo>

#include <vector>
#include <fstream>
#include <iostream>

#include "kmean.h"
#include "common/armavec.h"
#include "alternatepath.h"
#include "auction.h"
#include "auction_tree.h"

namespace po = boost::program_options;
po::options_description desc("Allowed options");
po::variables_map vm;

void process_options(int argc, char** argv);

template <typename vec>
void match()
{
  if (!vm.count("dat1") || !vm.count("dat2")) {
    std::cout << "need --dat1=[file1] --dat2=[file2]\n" << desc << "\n";
    return;
  }
  typedef Dataset<vec>                    dataset_type;
  dataset_type dat1, dat2;
  dat1.load(vm["dat1"].as<std::string>(), arma::arma_ascii);
  dat2.load(vm["dat2"].as<std::string>(), arma::arma_ascii);
  //dat1.print(); //dat2.print();


  if (vm["method"].as<std::string>() == "alternate") {
    typedef DistanceWeight<dataset_type>    costmatrix_type;
    costmatrix_type C(dat1, dat2);

    std::cout << "n1 = " << C.n1() << " n2 = " << C.n2() << "\n";
    std::cout << "Alternating path...\n";

    typedef AlternatePath<costmatrix_type>  algorithm_type;
    algorithm_type a(C);
    a.optimize();

    for (int i = 0; i < a.n(); i++)
      std::cout << i << " <--> " << a.next(i) << "\n";
  }
  else if (vm["method"].as<std::string>() == "auction") {
    typedef DistanceWeight<dataset_type>    costmatrix_type;
    costmatrix_type C(dat1, dat2);

    std::cout << "n1 = " << C.n1() << " n2 = " << C.n2() << "\n";
    std::cout << "Auction...\n";

    typedef Auction<costmatrix_type>  algorithm_type;
    algorithm_type a(C);
    a.optimize();

    for (int i = 0; i < a.n(); i++)
      std::cout << i << " <--> " << a.next(i) << "\n";
  }
  else {
    typedef TreeDistanceWeight<dataset_type>    costmatrix_type;
    costmatrix_type C(dat1, dat2);

    std::cout << "n1 = " << C.n1() << " n2 = " << C.n2() << "\n";
    std::cout << "Auction with tree...\n";

    typedef AuctionTree<costmatrix_type>  algorithm_type;
    algorithm_type a(C);
    a.optimize();

    for (int i = 0; i < a.n(); i++)
      std::cout << i << " <--> " << a.next(i) << "\n";
  }
}

template <typename vec>
void generateData()
{
  if (!vm.count("to")) {
    std::cout << "need --to=[file]\n" << desc << "\n";
    return;
  }
  if (vm.count("seed")) srand(time(NULL));
  int n = vm["n"].as<int>(); // #points in each cluster
  int nc = vm["nc"].as<int>(); // #clusters
  int d = vm["d"].as<int>();
  double r = vm["r"].as<double>();
  Dataset<vec> data;
  for (int c = 0; c < nc; c++) {
    vec center(d);
    center.randn();
    center *= 10.0;
    std::cerr << "c = " << c << " center=\n" << center;
    for (int i = 0; i < n; i++) {
      vec x(d);
      x.randn();
      x *= r;
      x += center;
      data.push_back(x);
    }
  }
  data.save(vm["to"].as<std::string>(), arma::arma_ascii);
}

int main(int argc, char** argv) {
  process_options(argc, argv);
  if (vm.count("help")) {
    std::cout << desc << "\n";
    return 0;
  }

  typedef ArmaVec vec;
  if (vm.count("gen")) {
    generateData <vec> ();
    return 0;
  }

  if (vm.count("match")) {
    boost::posix_time::ptime time_start(boost::posix_time::microsec_clock::local_time());
    match<vec> ();
    boost::posix_time::ptime time_end(boost::posix_time::microsec_clock::local_time());
    boost::posix_time::time_duration duration(time_end - time_start);
    std::cerr << "Duration = " << duration << "\n";
    return 0;
  }

  std::cout << "need --match or --gen\n" << desc << "\n";
  return 0;
}

void process_options(int argc, char** argv)
{
  desc.add_options()
    ("help", "produce this usage")
    ("to", po::value<std::string>(), "file contains training samples")
    ("gen", "generate random data")
    ("match", "match two datasets")
    ("method", po::value<std::string>()->default_value("auctree"), "matching method: auction, alternate, auctree")
    ("dat1", po::value<std::string>(), "file contains the first dataset")
    ("dat2", po::value<std::string>(), "file contains the second dataset")
    ("seed", "using different random seed")
    ("n", po::value<int>()->default_value(10), "number of points")
    ("nc", po::value<int>()->default_value(3), "number of centers")
    ("d", po::value<int>()->default_value(2), "input dimension")
//    ("r", po::value<double>()->default_value(1.0), "cluster radius")
//    ("lambda", po::value<double>()->default_value(1.0), "regularized parameter")
//    ("iter", po::value<int>()->default_value(10), "maximum number of iterations")
//    ("eta", po::value<double>()->default_value(0.5), "learning rate")
//    ("mesh", po::value<std::string>()->default_value("mesh"), "mesh files prefix")
    ;
  po::store(po::parse_command_line(argc, argv, desc), vm);
  po::notify(vm);
}
